
#include "xdns-data-impl.h"
#include <string.h>
#include <algorithm>

using namespace std;

XdnsDataImpl::XdnsDataImpl()
: XdnsData(),
  m_totalSize(0),
  m_blocks()
{
}

XdnsDataImpl::~XdnsDataImpl()
{
    Clear();
}

XdnsError
XdnsDataImpl::AppendData(unsigned char* buffer,
                         size_t bufferSize)
{
    if ((NULL == buffer) ||
        (0 == bufferSize))
    {
        return XE_Inval;
    }

    unsigned char* newBuffer  = new (nothrow) unsigned char[bufferSize];
    if (NULL == newBuffer)
    {
        return XE_NoMem;
    }
    memcpy(newBuffer, buffer, bufferSize * sizeof(unsigned char));

    // Save the new data block.
    m_blocks.push_back(XdnsDataBlock(newBuffer, bufferSize));

    // Update the total size of data.
    m_totalSize += bufferSize;

    return XE_Ok;
}

XdnsError
XdnsDataImpl::SetData(size_t position,
                      unsigned char* buffer,
                      size_t bufferSize)
{
    if ((NULL == buffer) ||
        (0 == bufferSize))
    {
        return XE_Inval;
    }

    if ((position + bufferSize) > GetDataSize())
    {
        return XE_Range;
    }

    size_t bufferOffset = 0;
    size_t remainedSize = bufferSize;
    vector<XdnsDataBlock>::iterator iter = m_blocks.begin();
    vector<XdnsDataBlock>::iterator end = m_blocks.end();
    for (; (iter != end) && (remainedSize > 0); ++iter)
    {
        size_t blockSize = iter->m_bufferSize;

        // Check if the position is located in this block.
        if (position >= blockSize)
        {
            position -= blockSize;
            continue;
        }

        size_t copiedSize = min(remainedSize, blockSize - position);
        memcpy(iter->m_buffer + position, buffer + bufferOffset, copiedSize);
        position = 0;
        bufferOffset += copiedSize;
        remainedSize -= copiedSize;
    }

    return XE_Ok;
}

size_t
XdnsDataImpl::GetData(unsigned char* buffer,
                      size_t position,
                      size_t requestedSize) const
{
    if ((NULL == buffer) ||
        (0 == requestedSize))
    {
        return 0;
    }
    
    size_t bufferOffset = 0;
    size_t remainedSize = requestedSize;
    vector<XdnsDataBlock>::const_iterator iter = m_blocks.begin();
    vector<XdnsDataBlock>::const_iterator end = m_blocks.end();
    for (; (iter != end) && (remainedSize > 0); ++iter)
    {
        size_t blockSize = iter->m_bufferSize;

        // Check if the position is located in this block.
        if (position >= blockSize)
        {
            position -= blockSize;
            continue;
        }

        size_t copiedSize = min(remainedSize, blockSize - position);
        memcpy(buffer + bufferOffset, iter->m_buffer + position, copiedSize);
        position = 0;
        bufferOffset += copiedSize;
        remainedSize -= copiedSize;
    }

    return (requestedSize - remainedSize);
}

size_t
XdnsDataImpl::GetDataSize() const
{
    return m_totalSize;
}

void
XdnsDataImpl::Clear()
{
    vector<XdnsDataBlock>::iterator iter = m_blocks.begin();
    vector<XdnsDataBlock>::iterator end = m_blocks.end();
    for (; iter != end; ++iter)
    {
        unsigned char* buffer = iter->m_buffer;
        if (buffer)
        {
            delete [] buffer;
        }
    }

    m_blocks.clear();

    m_totalSize = 0;
}

XdnsData*
XdnsDataImpl::Clone() const
{
    XdnsData* clonedData = NULL;
    XdnsError error = XdnsData::Init(&clonedData);
    if (error != XE_Ok)
    {
        return NULL;
    }

    vector<XdnsDataBlock>::const_iterator iter = m_blocks.begin();
    vector<XdnsDataBlock>::const_iterator end = m_blocks.end();
    for (; iter != end; ++iter)
    {
        const XdnsDataBlock& block = *iter;
        error = clonedData->AppendData(block.m_buffer,
                                       block.m_bufferSize);
        if (error != XE_Ok)
        {
            break;
        }
    }

    if (error != XE_Ok)
    {
        XdnsData::Release(clonedData);
        return NULL;
    }

    return clonedData;
}

XdnsDataBlock::XdnsDataBlock()
: m_buffer(NULL),
  m_bufferSize(0)
{
}

XdnsDataBlock::XdnsDataBlock(unsigned char* buffer,
                             size_t bufferSize)
: m_buffer(buffer),
  m_bufferSize(bufferSize)
{
}

XdnsDataBlock::XdnsDataBlock(const XdnsDataBlock& anotherBlock)
: m_buffer(anotherBlock.m_buffer),
  m_bufferSize(anotherBlock.m_bufferSize)
{
}

XdnsDataBlock::~XdnsDataBlock()
{
}

XdnsDataBlock&
XdnsDataBlock::operator=(const XdnsDataBlock& anotherBlock)
{
    if ((&anotherBlock) != this)
    {
        this->m_buffer = anotherBlock.m_buffer;
        this->m_bufferSize = anotherBlock.m_bufferSize;
    }

    return *this;
}

